/*
 * TetrisBoard.java
 *
 * This work is free software; you can redistribute it and/or
 * modify it under the terms of the Apache License as published 
 * by the Apache Software Foundation.
 *
 * This work is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * Apache License for more details.
 *
 * Copyright (c) 2011 Tran Hoang Anh (tranhoanganh89@gmail.com). 
 * All rights reserved.
 */
package net.hoanganh.jbraintetris;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

/**
 * Tetris board is a square board which contains a tetris piece. The board can
 * control the piece by move left, right, down, all down and rotate in case the
 * movement not collide with existed square. When the piece commit to board, all
 * piece squares become part of grid squares in the board.
 */
@SuppressWarnings("serial")
public class TetrisBoard extends SquareBoard {
	
	public final Color DEFAULT_GRID_COLOR = Color.gray.darker().darker(); 
	
	/**
	 * The piece controlled by this board.
	 */
	private Piece piece = null;

	/**
	 * The horizontal position of piece.
	 */
	private int xPos = 0;

	/**
	 * The vertical position of piece.
	 */
	private int yPos = 0;

	/**
	 * The grid color.
	 */
	private Color gridColor = DEFAULT_GRID_COLOR;

	/**
	 * Create new tetris board.
	 * 
	 * @param width
	 *            width in squares
	 * 
	 * @param height
	 *            height in squares
	 */
	public TetrisBoard(int width, int height) {
		super(width, height);
	}

	/**
	 * Get current piece.
	 * 
	 * @return the piece
	 */
	public Piece getPiece() {
		return piece;
	}

	/**
	 * Set and init position for piece. By default the piece is set to position
	 * that only show the bottom line of piece.
	 * 
	 * @param piece
	 *            the piece to set
	 * 
	 * @param isCenter
	 *            if true the piece is display at central of board.
	 */
	public void setPiece(Piece piece) {
		this.piece = piece;

		// Reset position
		xPos = yPos = 0;

		// Calculate position
		if (piece == null) {
			return;
		}

		xPos = getBoardSize().width / 2;
		Point[] positions = piece.getPositions();
		int max = 0;
		for (Point p : positions) {
			max = Math.max(max, p.y);
		}
		yPos = -max;
		redraw();
	}

	/**
	 * Get horizontal position of piece.
	 * 
	 * @return the xPos
	 */
	public int getxPos() {
		return xPos;
	}

	/**
	 * Set horizontal position of piece.
	 * 
	 * @param xPos
	 *            the xPos to set
	 */
	public void setxPos(int xPos) {
		if (xPos < 0 || xPos >= getBoardSize().width) {
			throw new IllegalArgumentException("yPos is not valid.");
		}

		this.xPos = xPos;
	}

	/**
	 * Get vertical position of piece.
	 * 
	 * @return the yPos
	 */
	public int getyPos() {
		return yPos;
	}

	/**
	 * Set vertical position of piece.
	 * 
	 * @param yPos
	 *            the yPos to set
	 */
	public void setyPos(int yPos) {
		if (yPos < 0 || yPos >= getBoardSize().height) {
			throw new IllegalArgumentException("yPos is not valid.");
		}

		this.yPos = yPos;
	}

	/**
	 * Check if specific line is full
	 * 
	 * @param y
	 *            y location in square
	 * 
	 * @return true if full, otherwise false
	 */
	public boolean isFullLine(int y) {
		for (int x = 0; x < getBoardSize().width; x++) {
			if (isEmpty(x, y)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Check if specific line is empty
	 * 
	 * @param y
	 *            y location in square
	 * 
	 * @return true if empty, otherwise false
	 */
	public boolean isEmptyLine(int y) {
		for (int x = 0; x < getBoardSize().width; x++) {
			if (!isEmpty(x, y)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Remove specific line
	 * 
	 * @param y
	 *            y location in square
	 */
	public void removeLine(int y) {
		for (int k = y - 1; k >= 0; k--) {
			for (int x = 0; x < getBoardSize().width; x++) {
				setSquareColor(x, k + 1, getSquareColor(x, k));
			}
		}

		for (int x = 0; x < getBoardSize().width; x++) {
			setSquareColor(x, 0, null);
		}
	}

	/**
	 * Remove all full lines.
	 * 
	 * @return number of line removed
	 */
	public int removeFullLine() {
		int cnt = 0;

		for (int y = 0; y < getBoardSize().height; y++) {
			if (isFullLine(y)) {
				removeLine(y);
				cnt++;
			}
		}
		return cnt;
	}

	/**
	 * Move piece one square left.
	 * 
	 * @return true if can move
	 */
	public boolean moveLeft() {
		if (piece == null || !canMoveTo(xPos - 1, yPos)) {
			return false;
		}
		xPos = xPos - 1;
		redraw();
		return true;
	}

	/**
	 * Move piece one square right.
	 * 
	 * @return true if can move
	 */
	public boolean moveRight() {
		if (piece == null || !canMoveTo(xPos + 1, yPos)) {
			return false;
		}
		xPos = xPos + 1;
		redraw();
		return true;
	}

	/**
	 * Move piece one square down
	 * 
	 * @return true if can move
	 */
	public boolean moveDown() {
		if (piece == null || !canMoveTo(xPos, yPos + 1)) {
			return false;
		}
		yPos = yPos + 1;
		redraw();
		return true;
	}

	/**
	 * Move piece down until collide with board square.
	 * 
	 * @return true if can move
	 */
	public int moveAllDown() {
		int cnt = 0;
		while (moveDown()) {
			cnt++;
		}
		redraw();
		return cnt;
	}

	public boolean isLanded() {
		return !canMoveTo(xPos, yPos + 1);
	}

	/**
	 * Rotate piece counter clockwise.
	 * 
	 * @return true if can move
	 */
	public boolean rotate() {
		if (piece == null) {
			return false;
		}

		int old = piece.getOrientation();
		piece.rotate();
		if (!canMoveTo(xPos, yPos)) {
			piece.setOrientation(old);
			return false;
		}
		redraw();
		return true;
	}

	/**
	 * Commit piece to square board.
	 */
	public void commit() {
		// Check piece
		if (piece == null) {
			return;
		}

		// Set piece square to board
		Point[] positions = piece.getPositions();
		for (Point p : positions) {
			int squareX = p.x + xPos;
			int squareY = p.y + yPos;
			setSquareColor(squareX, squareY, piece.getColor());
		}

		// Reset piece
		piece = null;
		redraw();
	}

	/**
	 * Check if can move piece to specific location.
	 * 
	 * @param x
	 *            horizontal location in board
	 * 
	 * @param y
	 *            vertical location in board
	 * 
	 * @return true if can move
	 */
	public boolean canMoveTo(int x, int y) {
		// Check piece
		if (piece == null) {
			return false;
		}

		// Check colliding with other squares or out of board
		Point[] positions = piece.getPositions();
		for (Point p : positions) {
			int squareX = p.x + x;
			int squareY = p.y + y;

			if (squareX < 0 || squareX >= getBoardSize().width
					|| squareY >= getBoardSize().height) {
				return false;
			}
			if (!isEmpty(squareX, squareY)) {
				return false;
			}
		}

		return true;
	}

	@Override
	protected void paintContent(Graphics g) {
		super.paintContent(g);

		if (piece != null) {
			Point[] positions = piece.getPositions();
			for (Point p : positions) {
				int squareX = p.x + xPos;
				int squareY = p.y + yPos;
				
				paintSquare(g, squareX, squareY, piece.getColor());
			}
		}
	}	
	
	@Override
	protected void paintBackground(Graphics g) {
		super.paintBackground(g);
		
		g.setColor(gridColor);
		for (int x=1; x<getBoardSize().width; x++) {
			int px = getInset().left + x * getSquareSize().width;
			g.drawLine(
				px,
				0,
				px,
				getHeight()
			);
		}
		
		for (int y=1; y<getBoardSize().height; y++) {
			int py = getInset().top + y * getSquareSize().height;
			g.drawLine(
				0,
				py,
				getWidth(),
				py
			);
		}
	}
}
